Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Displaying Raw • Download


meshchatx/src/backend/gif_utils.py fa46dc4ce9ab50cbe66a7a9e4b3bca0140102345 (fa46dc4c) Text, 5.63 KB

T8b949e# SPDX-License-Identifier: 0BSD

T8b949e"""Validation, hashing, and export/import helpers for user GIF library entries.

GIFs are stored per identity in the user_gifs table. Compared to stickers,
the library is intended for animated content shared in chats, so:

* Only animated-friendly formats are accepted (gif and webp).
* The per-file byte limit is larger.
* A usage_count is tracked at the DAO level so the picker can surface
most-used GIFs first.
"""

Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations

Tff7b72import T7ee787base64
Tff7b72import T7ee787hashlib

Te6edf3MAX_GIF_BYTES Tff7b72= T79c0ff5 Tff7b72* T79c0ff1024 Tff7b72* T79c0ff1024
Te6edf3MAX_GIFS_PER_IDENTITY Tff7b72= T79c0ff1000

Te6edf3_ALLOWED_TYPES Tff7b72= Tffa657frozensetTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffgifTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffwebpTa5d6ff"Tb4b4b4}Tb4b4b4)

Te6edf3_TYPE_ALIASES Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffgifTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffgifTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffwebpTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffwebpTa5d6ff"Tb4b4b4,
Tb4b4b4}


Tff7b72def Td2a8ffnormalize_image_typeTb4b4b4(Te6edf3image_typeTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4) Tff7b72-Tff7b72> Tffa657str Tff7b72| Tff7b72NoneTb4b4b4:
Tff7b72if Tff7b72not Te6edf3image_typeTb4b4b4:
Tff7b72return Tff7b72None
Te6edf3t Tff7b72= Tffa657strTb4b4b4(Te6edf3image_typeTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)
Te6edf3t Tff7b72= Te6edf3tTff7b72.Td2a8ffremoveprefixTb4b4b4(Ta5d6ff"Ta5d6ffimage/Ta5d6ff"Tb4b4b4)
Te6edf3t Tff7b72= Te6edf3_TYPE_ALIASESTff7b72.Td2a8ffgetTb4b4b4(Te6edf3tTb4b4b4, Te6edf3tTb4b4b4)
Tff7b72return Te6edf3t Tff7b72if Te6edf3t Tff7b72in Te6edf3_ALLOWED_TYPES Tff7b72else Tff7b72None


Tff7b72def Td2a8ffcontent_hash_hexTb4b4b4(Te6edf3image_bytesTb4b4b4: Tffa657bytesTb4b4b4) Tff7b72-Tff7b72> Tffa657strTb4b4b4:
Tff7b72return Te6edf3hashlibTff7b72.Td2a8ffsha256Tb4b4b4(Te6edf3image_bytesTb4b4b4)Tff7b72.Td2a8ffhexdigestTb4b4b4(Tb4b4b4)


Tff7b72def Td2a8ffdetect_image_format_from_magicTb4b4b4(Te6edf3image_bytesTb4b4b4: Tffa657bytesTb4b4b4) Tff7b72-Tff7b72> Tffa657str Tff7b72| Tff7b72NoneTb4b4b4:
T8b949e"""Detect a GIF-library compatible image format from magic bytes.

Returns a normalized type key (gif or webp), or None.
"""
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3image_bytesTb4b4b4, Tb4b4b4(Tffa657bytesTb4b4b4, Tffa657bytearrayTb4b4b4)Tb4b4b4) Tff7b72or Tffa657lenTb4b4b4(Te6edf3image_bytesTb4b4b4) Tff7b72< T79c0ff4Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3b Tff7b72= Tffa657bytesTb4b4b4(Te6edf3image_bytesTb4b4b4)
Tff7b72if Tffa657lenTb4b4b4(Te6edf3bTb4b4b4) Tff7b72>Tff7b72= T79c0ff6 Tff7b72and Te6edf3bTb4b4b4[T79c0ff0Tb4b4b4:T79c0ff6Tb4b4b4] Tff7b72in Tb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffGIF87aTa5d6ff"Tb4b4b4, Ta5d6ffbTa5d6ff"Ta5d6ffGIF89aTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6ffgifTa5d6ff"
Tff7b72if Tffa657lenTb4b4b4(Te6edf3bTb4b4b4) Tff7b72>Tff7b72= T79c0ff12 Tff7b72and Te6edf3bTb4b4b4[T79c0ff0Tb4b4b4:T79c0ff4Tb4b4b4] Tff7b72== Ta5d6ffbTa5d6ff"Ta5d6ffRIFFTa5d6ff" Tff7b72and Te6edf3bTb4b4b4[T79c0ff8Tb4b4b4:T79c0ff12Tb4b4b4] Tff7b72== Ta5d6ffbTa5d6ff"Ta5d6ffWEBPTa5d6ff"Tb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6ffwebpTa5d6ff"
Tff7b72return Tff7b72None


Tff7b72def Td2a8ffvalidate_gif_payloadTb4b4b4(
Te6edf3image_bytesTb4b4b4: Tffa657bytesTb4b4b4,
Te6edf3image_typeTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657tupleTb4b4b4[Tffa657strTb4b4b4, Tffa657strTb4b4b4]Tb4b4b4:
T8b949e"""Returns (normalized_image_type, content_hash_hex).

The declared image_type must match the format detected from magic
bytes; the stored type is the normalized detected format.

Raises ValueError with a short reason on invalid input.
"""
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3image_bytesTb4b4b4, Tb4b4b4(Tffa657bytesTb4b4b4, Tffa657bytearrayTb4b4b4)Tb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffinvalid_image_bytesTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72if Tffa657lenTb4b4b4(Te6edf3image_bytesTb4b4b4) Tff7b72== T79c0ff0Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffempty_imageTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72if Tffa657lenTb4b4b4(Te6edf3image_bytesTb4b4b4) Tff7b72> Te6edf3MAX_GIF_BYTESTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffimage_too_largeTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3nt Tff7b72= Te6edf3normalize_image_typeTb4b4b4(Te6edf3image_typeTb4b4b4)
Tff7b72if Tff7b72not Te6edf3ntTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffinvalid_image_typeTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3detected Tff7b72= Te6edf3detect_image_format_from_magicTb4b4b4(Te6edf3image_bytesTb4b4b4)
Tff7b72if Tff7b72not Te6edf3detectedTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffinvalid_image_signatureTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72if Te6edf3detected Tff7b72!= Te6edf3ntTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffmagic_type_mismatchTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3h Tff7b72= Te6edf3content_hash_hexTb4b4b4(Tffa657bytesTb4b4b4(Te6edf3image_bytesTb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3detectedTb4b4b4, Te6edf3h


Te6edf3_EXPORT_FORMAT Tff7b72= Ta5d6ff"Ta5d6ffmeshchatx-gifsTa5d6ff"
Te6edf3_EXPORT_VERSION Tff7b72= T79c0ff1


Tff7b72def Td2a8ffvalidate_export_documentTb4b4b4(Te6edf3dataTb4b4b4: Tffa657objectTb4b4b4) Tff7b72-Tff7b72> Tffa657listTb4b4b4[Tffa657dictTb4b4b4]Tb4b4b4:
T8b949e"""Parse and validate a GIF library export JSON document.

Each entry has name, image_type, image_bytes (base64), and
optional source_message_hash and usage_count.
"""
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3dataTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffinvalid_documentTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72if Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffformatTa5d6ff"Tb4b4b4) Tff7b72!= Te6edf3_EXPORT_FORMATTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffinvalid_formatTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3ver Tff7b72= Tffa657intTb4b4b4(Te6edf3dataTb4b4b4[Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4]Tb4b4b4)
Tff7b72except Tb4b4b4(Tf85149KeyErrorTb4b4b4, Tf85149TypeErrorTb4b4b4, Tf85149ValueErrorTb4b4b4) Tff7b72as Te6edf3excTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffunsupported_versionTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4) Tff7b72from T7ee787exc
Tff7b72if Te6edf3ver Tff7b72!= Te6edf3_EXPORT_VERSIONTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffunsupported_versionTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3gifs Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffgifsTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3gifsTb4b4b4, Tffa657listTb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffinvalid_gifs_arrayTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3outTb4b4b4: Tffa657listTb4b4b4[Tffa657dictTb4b4b4] Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3iTb4b4b4, Te6edf3item Tff7b72in Tffa657enumerateTb4b4b4(Te6edf3gifsTb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3itemTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffinvalid_gif_at_Tffd700{Te6edf3iTffd700}Ta5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Te6edf3name Tff7b72= Te6edf3itemTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4)
Te6edf3image_type Tff7b72= Te6edf3itemTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffimage_typeTa5d6ff"Tb4b4b4)
Te6edf3image_b64 Tff7b72= Te6edf3itemTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffimage_bytesTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3image_b64Tb4b4b4, Tffa657strTb4b4b4) Tff7b72or Tff7b72not Te6edf3image_b64Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffmissing_image_bytes_at_Tffd700{Te6edf3iTffd700}Ta5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3base64Tff7b72.Td2a8ffb64decodeTb4b4b4(Te6edf3image_b64Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3validateTff7b72=Tff7b72FalseTb4b4b4)
Tff7b72except Tb4b4b4(Tf85149ValueErrorTb4b4b4, Tf85149TypeErrorTb4b4b4) Tff7b72as Te6edf3excTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffinvalid_base64_at_Tffd700{Te6edf3iTffd700}Ta5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4) Tff7b72from T7ee787exc
Te6edf3src Tff7b72= Te6edf3itemTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffsource_message_hashTa5d6ff"Tb4b4b4)
Te6edf3usage Tff7b72= Te6edf3itemTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffusage_countTa5d6ff"Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3usage_int Tff7b72= Tffa657intTb4b4b4(Te6edf3usageTb4b4b4) Tff7b72if Te6edf3usage Tff7b72is Tff7b72not Tff7b72None Tff7b72else T79c0ff0
Tff7b72except Tb4b4b4(Tf85149TypeErrorTb4b4b4, Tf85149ValueErrorTb4b4b4)Tb4b4b4:
Te6edf3usage_int Tff7b72= T79c0ff0
Te6edf3usage_int Tff7b72= Tffa657maxTb4b4b4(Te6edf3usage_intTb4b4b4, T79c0ff0Tb4b4b4)
Te6edf3outTff7b72.Td2a8ffappendTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4: Te6edf3name Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3nameTb4b4b4, Tffa657strTb4b4b4) Tff7b72else Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffimage_typeTa5d6ff"Tb4b4b4: Te6edf3image_typeTb4b4b4,
Ta5d6ff"Ta5d6ffimage_bytes_b64Ta5d6ff"Tb4b4b4: Te6edf3image_b64Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffsource_message_hashTa5d6ff"Tb4b4b4: Te6edf3src Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3srcTb4b4b4, Tffa657strTb4b4b4) Tff7b72else Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffusage_countTa5d6ff"Tb4b4b4: Te6edf3usage_intTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3out


Tff7b72def Td2a8ffbuild_export_documentTb4b4b4(Te6edf3gifsTb4b4b4: Tffa657listTb4b4b4[Tffa657dictTb4b4b4]Tb4b4b4, Te6edf3exported_at_isoTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657dictTb4b4b4:
T8b949e"""Build the GIF export document.

gifs: rows with name, image_type, image_bytes (base64 str),
source_message_hash, and usage_count.
"""
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffformatTa5d6ff"Tb4b4b4: Te6edf3_EXPORT_FORMATTb4b4b4,
Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4: Te6edf3_EXPORT_VERSIONTb4b4b4,
Ta5d6ff"Ta5d6ffexported_atTa5d6ff"Tb4b4b4: Te6edf3exported_at_isoTb4b4b4,
Ta5d6ff"Ta5d6ffgifsTa5d6ff"Tb4b4b4: Te6edf3gifsTb4b4b4,
Tb4b4b4}


Tff7b72def Td2a8ffmime_for_image_typeTb4b4b4(Te6edf3normalized_typeTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657strTb4b4b4:
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffgifTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffimage/gifTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffwebpTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffimage/webpTa5d6ff"Tb4b4b4,
Tb4b4b4}Tff7b72.Td2a8ffgetTb4b4b4(Te6edf3normalized_typeTb4b4b4, Ta5d6ff"Ta5d6ffapplication/octet-streamTa5d6ff"Tb4b4b4)


Tff7b72def Td2a8ffsanitize_gif_nameTb4b4b4(Te6edf3nameTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4) Tff7b72-Tff7b72> Tffa657str Tff7b72| Tff7b72NoneTb4b4b4:
Tff7b72if Te6edf3name Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Tff7b72None
Te6edf3s Tff7b72= Ta5d6ff"Ta5d6ff"Tff7b72.Td2a8ffjoinTb4b4b4(Te6edf3ch Tff7b72for Te6edf3ch Tff7b72in Tffa657strTb4b4b4(Te6edf3nameTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72if Te6edf3chTff7b72.Td2a8ffisprintableTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3sTb4b4b4:
Tff7b72return Tff7b72None
Tff7b72return Te6edf3sTb4b4b4[Tb4b4b4:T79c0ff128Tb4b4b4]


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────